/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algoritmogeneticoCHC;

import java.util.*;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;

public class NewMain {

    static int opcion = 3;             //Número de función a optimizar
    static int numGen = 300;             //Número de generaciones
    static int numInds = 100;           //Número de individuos
    static int bitsDec = 4;            //Bits para decimales
    static int bitsEnt = 6;            //Bits para enteros
    static int numVar = 2;             //Número de variables por genoma
    static int semilla = 210195;       //Semilla para los números aleatorios
    static int denominadorThreshold = 4; //El threshold va a ser L/denominadorThreshold. Originalmente debe ser 4. 
    static double probMuta = 0.35;    //Probabilidad de mutación cataclísimica. Se recomienda .35
    static boolean minimizar = true;  //Se escoge minimizar el problema con true y maximizar con false

    public static void Menu(Scanner scan) {
        int modificaParam = 1;
        ImprimeParametros();
        System.out.println("\n¿Quieres modificar algún parámetro? (1=sí, 0=no)");
        modificaParam = scan.nextInt();
        while (modificaParam == 1) {
            ModificaParametros();
            ImprimeParametros();
            System.out.println("\n¿Quieres modificar algún parámetro? (1=sí, 0=no)");
            modificaParam = scan.nextInt();
        }
    }

    public static void ImprimeParametros(/*Scanner scan, int opcion, int numGen, int numInds, int bitsDec, int bitsEnt, int numVar, int semilla, int normaliza, double probxover, double probmuta*/) {
        System.out.println("\nParámetros del algoritmo: ");
        System.out.println(" 1.Función a optimizar: " + opcion);
        System.out.println(" 2.Número de generaciones: " + numGen);
        System.out.println(" 3.Número de individuos: " + numInds);
        System.out.println(" 4.Bits para decimales: " + bitsDec);
        System.out.println(" 5.Bits para enteros: " + bitsEnt);
        System.out.println(" 6.Número de variables por genoma: " + numVar);
        System.out.println(" 7.Semilla para los números aleatorios: " + semilla);
        System.out.println(" 8.Denominador threshold (El threshold va a ser L/denominadorThreshold): " + denominadorThreshold);
        System.out.println("9.Probabilidad de mutación: " + probMuta);
        System.out.println("10.Minimizar (true)/ Maximizar (false): " + minimizar);
    }

    public static void ModificaParametros() {
        Scanner sca = new Scanner(System.in);
        int modifica;
        System.out.println("\n¿Qué parámetro quieres modificar?");
        int param = sca.nextInt();
        System.out.println("¿Cuál es el nuevo valor?");
        switch (param) {
            case 1:
                opcion = sca.nextInt();
                break;
            case 2:
                numGen = sca.nextInt();
                break;
            case 3:
                numInds = sca.nextInt();
                break;
            case 4:
                bitsDec = sca.nextInt();
                break;
            case 5:
                bitsEnt = sca.nextInt();
                break;
            case 6:
                numVar = sca.nextInt();
                break;
            case 7:
                semilla = sca.nextInt();
                break;
            case 8:
                denominadorThreshold = sca.nextInt();
                break;
            case 9:
                probMuta = sca.nextDouble();
                break;
            case 10:
                int min = sca.nextInt();
                minimizar = (min == 1);
                break;
        }
    }

    public static void SalvaEsquemasArchivo(SchemaTool[] esquemas) {
        ZFile archivo = new ZFile();
        archivo.OpenLocal("esquema.txt", ZFileMode.WRITE);
        for (int i = 0; i < esquemas.length; i++) {
            archivo.WriteLn(String.format("%s\t(%d)", esquemas[i], i));
        }
        archivo.Close();
    }

    public static void UserRun() {
        Scanner scan = new Scanner(System.in);
        int otravez = 1;
        String renglon = "N,G,Pm,ThresholdInicial,NumVar,";
        for (int i = 0; i < numVar; i++) {
            renglon = renglon + "x" + i + ",";
        }
        renglon = renglon + "fitness\n";
        while (otravez == 1) {
            Random aleatorio = new Random();        //Inicialización de generador de números pseudoaleaotrios
            aleatorio.setSeed(semilla);             //Inicializar la semilla del generador.

            Menu(scan);                             //El input del usuario se almacena en las variables globales

            //Declarar las variables tipo Población
            PoblacionCHC pob1 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);
            PoblacionCHC pob2 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);
            PoblacionCHC pob3 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);
            PoblacionCHC pob4 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);
            //Declarar threshold
            int threshold = (int) ((bitsEnt + bitsDec + 1) * numVar) / denominadorThreshold;
            //Crear la población inicial
            pob1.CreaPoblacionCHCInicial();
            /*//Quitar este comentario para ver los genomas de la población inicial
             System.out.println("\nImprimiendo población Inicial");
             pob1.ImprimeGenomas();*/
            //Crea el registro de esquemas
            SchemaTool[] esquemas = new SchemaTool[numGen];
            boolean[] cataIndex = new boolean[numGen];
            for (int clearIndex = 0; clearIndex < cataIndex.length; clearIndex++) {
                cataIndex[clearIndex] = false;
            }
            //Iterar el proceso evolutivo
            for (int i = 0; i < numGen; i++) {
                //Encontrar el fitness y ordenar de mejor a peor
                pob1.Fitness(opcion);
                pob1.OrdenaPorFitness();
                //pob1.ImprimePoblacionCHC();

                //Crear nueva población copiando los elementos de pob1
                pob2 = new PoblacionCHC(pob1);
                //Esta función genera la población C del algoritmo CHC.
                //Ya están mutados los individuos que hay que mutar.
                //Loso individuos que se eliminan tienen "eliminar = true" aunque siguen en la población.
                pob2.PobCHUX();
                pob2.Fitness(opcion);
                //pob2.ImprimePoblacionCHC();

                //Contamos cuántos individuos quedan sin eliminar (parámetro necesario para 
                //crear la población P' = P U C
                int n = pob2.TamanoReal();
                //Creamos la población P' = P U C, la ordenamos y seleccionamos a los N mejores.
                pob3 = new PoblacionCHC(pob1, pob2, n);
                //pob3.ImprimePoblacionCHC();

                pob3.OrdenaPorFitness();
                pob4.SeleccionaPrimerosN(pob3, numInds);
                //Comparamos entre P y P'
                if (pob4.Iguales(pob1)) {
                    //Si son iguales disminuimos el umbral.
                    threshold = threshold - 1;
                    if (threshold == 0) {
                        //Hay que hacer una mutación cataclísmica
                        pob4.MutacionCataclismica();
                        //Reiniciamos el umbral
                        threshold = (int) ((bitsEnt + bitsDec + 1) * numVar) / denominadorThreshold;
                        cataIndex[i] = true;
                    }
                }
                pob1 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);
                pob1 = new PoblacionCHC(pob4);
                esquemas[i] = pob1.ObtenEsquema();
                /* //Quitar este comentario para ver la población
                 System.out.println("\nImprimiendo población "+i+".");
                 pob4.ImprimePoblacionConValores();*/
            }

            //Imprimer comportaiento de los esquemas
            SalvaEsquemasArchivo(esquemas);
            //Volvemos a sacar el fitness y a ordenar por el fitness
            pob1.Fitness(opcion);
            pob1.OrdenaPorFitness();
            /* //Quitar este comentario para ver la población final
             System.out.println("Imprimieno población 1");
             pob1.ImprimePoblacionConValores();*/

            //Imprimir el resutlado en pantalla
            pob1.ImprimeOptimo();
            pob1.individuos[0].ImprimeGenoma();

            double valores[] = pob1.individuos[0].RegresaValores();

            //Guardar los valores para imprimir el resultado en archivo
            threshold = (int) ((bitsEnt + bitsDec + 1) * numVar) / denominadorThreshold;
            renglon = renglon + numInds + "," + numGen + "," + probMuta + "," + threshold + "," + numVar;
            for (int i = 0; i < numVar; i++) {
                renglon = renglon + "," + valores[i];
            }
            renglon = renglon + "," + pob3.RegresaOptimoString() + "\n";

            System.out.println("\n¿Quieres que vuelva a correr? (1=sí, 0=no)");
            otravez = scan.nextInt();
        }

        //En esta parte se imprimen en un archivo csv los valores de Número de individuos,
        //número de generaciones, prob. mutación, prob. crossover y fitness.
        FileWriter fw;
        try {
            fw = new FileWriter("AlgoritmoGeneticoCHC\\DeJong4.csv");
            PrintWriter pw = new PrintWriter(fw);
            pw.print(renglon);
            pw.flush();
            pw.close();
            fw.close();
        } catch (IOException ex) {
            Logger.getLogger(NewMain.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("No imprimió archivo.");
        }
    }

    /*
     * La prueba que se hara con diferentes parametros y generacion fija
     */
    public static void ParameterTest() {
        //Constantes del experimento
        numGen = 500;
        numInds = 100;
        bitsEnt = 4;
        bitsDec = 40;
        numVar = 2;
        opcion = 4; //Problema (1) de la Suite de Funciones
        minimizar = true;
        
        Random aleatorio = new Random();        //Inicialización de generador de números pseudoaleaotrios
        aleatorio.setSeed(semilla);             //Inicializar la semilla del generador.

        //File writer
        ZFile archivoRes = new ZFile();
        archivoRes.OpenLocal("ParameterTest.csv", ZFileMode.WRITE);
        String renglon = "X,Y,Fitness,Threshold,Pm";
        archivoRes.WriteLn(renglon);
        archivoRes.Close();
        int threshold, expIndex = 0;

        for (int thIndex = 1; thIndex <= 45; thIndex += 4) {
            threshold = thIndex;
            for (double pmIndex = 0.2; pmIndex <= 0.5; pmIndex += 0.03) {
                probMuta = pmIndex;
                //Repite 50 veces con cada configuración
                for (int iter = 0; iter < 30; iter++) {
                    expIndex++;
                    System.out.printf("Experimento: %d  Th:%d   Pm: %f\n", expIndex, thIndex, pmIndex);
                    //Declarar las variables tipo Población
                    PoblacionCHC pob1 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);
                    PoblacionCHC pob2 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);
                    PoblacionCHC pob3 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);
                    PoblacionCHC pob4 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);

                    //Crear la población inicial
                    pob1.CreaPoblacionCHCInicial();

                    //Iterar el proceso evolutivo
                    for (int i = 0; i < numGen; i++) {
                        //Encontrar el fitness y ordenar de mejor a peor
                        pob1.Fitness(opcion);
                        pob1.OrdenaPorFitness();
                        //pob1.ImprimePoblacionCHC();

                        //Crear nueva población copiando los elementos de pob1
                        pob2 = new PoblacionCHC(pob1);
                        //Esta función genera la población C del algoritmo CHC.
                        //Ya están mutados los individuos que hay que mutar.
                        //Loso individuos que se eliminan tienen "eliminar = true" aunque siguen en la población.
                        pob2.PobCHUX();
                        pob2.Fitness(opcion);
                        //pob2.ImprimePoblacionCHC();

                        //Contamos cuántos individuos quedan sin eliminar (parámetro necesario para 
                        //crear la población P' = P U C
                        int n = pob2.TamanoReal();
                        //Creamos la población P' = P U C, la ordenamos y seleccionamos a los N mejores.
                        pob3 = new PoblacionCHC(pob1, pob2, n);
                        //pob3.ImprimePoblacionCHC();

                        pob3.OrdenaPorFitness();
                        pob4.SeleccionaPrimerosN(pob3, numInds);
                        //Comparamos entre P y P'
                        if (pob4.Iguales(pob1)) {
                            //Si son iguales disminuimos el umbral.
                            threshold = threshold - 1;
                            if (threshold == 0) {
                                //Hay que hacer una mutación cataclísmica
                                pob4.MutacionCataclismica();
                                //Reiniciamos el umbral
                                threshold = (int) ((bitsEnt + bitsDec + 1) * numVar) / denominadorThreshold;
                            }
                        }
                        pob1 = new PoblacionCHC(numInds, bitsEnt, bitsDec, numVar, denominadorThreshold, probMuta, aleatorio, minimizar);
                        pob1 = new PoblacionCHC(pob4);
                    }

                    //Volvemos a sacar el fitness y a ordenar por el fitness
                    pob1.Fitness(opcion);
                    pob1.OrdenaPorFitness();

                    //Elite
                    double fitness = pob1.individuos[0].fitness;
                    double valores[] = pob1.individuos[0].RegresaValores();

                    renglon = String.format("%f,%f,%f,%d,%f",
                            fitness, valores[0], valores[1], thIndex, pmIndex);

                    archivoRes.OpenLocal("ParameterTest.csv", ZFileMode.APPEND);
                    archivoRes.WriteLn(renglon);
                    archivoRes.Close();
                }
            }//FOR pmIndex
        }//FOR thIndex
    }

    public static void DataBot() {
    }

    public static void main(String[] args) {
        //UserRun();
        ParameterTest();
        //DataBot();
    }
}
